package com.spring;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;

public class ApplicationContext {

    ConcurrentHashMap<String, Object> singletonObjecs = new ConcurrentHashMap<>();
    ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    ConcurrentLinkedDeque<BeanPostProcessor> beanPostProcessors = new ConcurrentLinkedDeque<>();
    private Class aClass;

    public ApplicationContext(Class aClass) {
        this.aClass = aClass;
        // 拿到配置类,然后去解析配置
        scan(aClass);
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(entry.getKey());
            if (beanDefinition.getScope().equals("singleton")) {
                singletonObjecs.put(entry.getKey(), createBean(entry.getKey(), beanDefinition));
            }
        }
    }

    private Object createBean(String beanName, BeanDefinition bean) {
        Object object = null;
        try {
            Class clazz = bean.getClazz();

            // 创建一个bean
            object = clazz.getDeclaredConstructor().newInstance();

            // 初始化之前加工
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                object = beanPostProcessor.postProcessBeforeInitialization(object, beanName);
            }

            // javabean 名称回调
            if (object instanceof BeanNameAware) {
                ((BeanNameAware) object).setBeanName(beanName);
            }
            // javabean 初始化回调
            if (object instanceof InitializingBean) {
                try {
                    ((InitializingBean) object).afterPropertiesSet();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            // 初始化属性
            for (Field field : clazz.getDeclaredFields()) {
                if (Objects.nonNull(field)) {
                    if (field.isAnnotationPresent(Autowired.class)) {
                        Class<?> aClass = field.getType();
                        // 根据filed拿到他的类
                        if (aClass.isAnnotationPresent(Component.class)) {
                            Component declaredAnnotation = aClass.getDeclaredAnnotation(Component.class);
                            String name = declaredAnnotation.name();
                            BeanDefinition beanDefinition = beanDefinitionMap.get(name);
                            if (beanDefinition.getScope().equals("singleton")) {
                                field.setAccessible(true);
                                field.set(object, singletonObjecs.get(name));
                            } else {
                                field.setAccessible(true);
                                field.set(object, createBean(name, beanDefinition));
                            }
                        }
                    }
                }
            }

            // 初始化之后加工
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                object = beanPostProcessor.postProcessAfterInitialization(object, beanName);
            }

        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
        return object;
    }

    public void scan(Class aClass) {
        ComponentScan com = (ComponentScan) this.aClass.getDeclaredAnnotation(ComponentScan.class);
        if (Objects.nonNull(com)) {
            // 获取扫描路径
            String path = com.scan();
            path = path.replace(".", "/");
            ClassLoader classLoader = ApplicationContext.class.getClassLoader();
            URL systemResource = ClassLoader.getSystemResource(path);
            File file = new File(systemResource.getFile());
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                assert files != null;
                for (File file1 : files) {
                    if (file1.isFile() && file1.getName().endsWith(".class")) {
                        path = path.replace("/", ".");
                        Class<?> clazz = null;
                        try {
                            clazz = classLoader.loadClass(path + "." + file1.getName().replace(".class", ""));
                            if (clazz.isAnnotationPresent(Component.class)) {
                                // BeanDefinition  解析类
                                Component comp = clazz.getDeclaredAnnotation(Component.class);
                                String name = comp.name();
                                if (name.length() <= 0) {
                                    String[] split = clazz.getName().split("\\.");
                                    String name1 = split[split.length - 1];
                                    String substring = name1.substring(0, 1);
                                    String substring1 = name1.substring(1, name1.length());
                                    substring = substring.toLowerCase(Locale.ROOT);
                                    name = substring.concat(substring1);
                                }
                                BeanDefinition bean = new BeanDefinition();
                                bean.setClazz(clazz);
                                if (clazz.isAnnotationPresent(Scope.class)) {
                                    Scope scope = clazz.getDeclaredAnnotation(Scope.class);
                                    bean.setScope(scope.value());
                                } else {
                                    bean.setScope("singleton");
                                }
                                beanDefinitionMap.put(name, bean);

                                if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                    beanPostProcessors.add((BeanPostProcessor) createBean(name, bean));
                                }
                            }
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    public Object getBean(String name) {
        if (singletonObjecs.containsKey(name)) {
            BeanDefinition beandef = beanDefinitionMap.get(name);
            if (Objects.nonNull(beandef) &&
                    beandef.getScope().equals("singleton")) {
                return singletonObjecs.get(name);
            } else {
                // 创建一个对象
                return createBean(name, beandef);
            }
        }
        throw new RuntimeException("找不到对象!" + name);
    }

//    public <T> T  getBean(String name,Class<T> clazz){
//        if (singletonObjecs.containsKey(name)) {
//            BeanDefinition beandef = beanDefinitionMap.get(name);
//            if (Objects.nonNull(beandef) &&
//                    beandef.getScope().equals("singleton")) {
//                return (T) singletonObjecs.get(name);
//            }else{
//                // 创建一个对象
//                return (T) createBean(beandef);
//            }
//        } throw new RuntimeException("找不到对象!" + name);
//    }
}
